package cn.cgj.flowable.service.impl;

import cn.cgj.flowable.service.FlowableService;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * flowable流程服务实现
 */
@Service
public class FlowableServiceImpl implements FlowableService {

    /**
     * 日志
     */
    private static final Logger log = LoggerFactory.getLogger(FlowableServiceImpl.class);

    /**
     *  主要表介绍
     *  ACT_RU_TASK： 每次启动的流程都会再这张表中,表示代办项, 流程结束会删除该流程数据
     *  ACT_RU_EXECUTION： 流程执行过程表, 会存该流程正在执行的过程数据, 流程结束会删除该流程数据
     *  ACT_RU_VARIABLE： 流程变量表, 流程中传的参数都会再该表存储, 流程结束会删除该流程数据
     *  ACT_HI_PROCINST： 历史运行流程, 当流程处理完了, 在ACT_RU_* 表中就不会有数据, 可以在该表中查询历史
     *  ACT_HI_TASKINST： 历史运行的task信息,
     *  ACT_RE_PROCDEF： 流程模板记录,同一个key多次发布version_字段会递增
     *  ACT_RE_DEPLOYMENT： 部署的流程模板, 可以启动流程使用的
     */


    /**
     * 主要用于管理流程实例的运行时数据。
     * 这包括启动新的流程实例、查询流程实例、挂起和激活流程实例、查询流程实例的状态等。
     * 通过这个服务，你可以控制流程实例的整个生命周期，包括它们的开始、暂停、恢复和结束。
     */
    @Autowired
    private RuntimeService runtimeService;

    /**
     * 主要用于管理与人（用户或组）相关的任务。
     * 这包括查询分配给用户或组的任务、创建任务、完成任务、删除任务、查询任务的历史等。在流程执行过程中，任务通常代表需要人工干预的步骤。
     * 通过这个服务，你可以实现任务的分配、跟踪和完成。
     */
    @Autowired
    private TaskService taskService;

    /**
     * 用于管理流程定义和部署。
     * 它允许你部署流程定义（通常是 BPMN 2.0 XML 文件），查询已部署的流程定义，暂停和激活流程定义等。
     * 流程定义是描述业务流程的模型，而 RepositoryService 就是用来管理这些模型在 Flowable 中的存储和状态的。
     */
    @Autowired
    private RepositoryService repositoryService;

    /**
     * ProcessEngine 是 Flowable 架构中的核心接口，它提供了一个统一的访问点来访问上述所有服务（RuntimeService、TaskService、RepositoryService 等）
     * 以及其他服务（如 HistoryService、IdentityService 等）。
     * ProcessEngine 是流程引擎的实例，它封装了 Flowable 的所有功能。
     */
    @Autowired
    private ProcessEngine processEngine;

    @Override
    public ProcessInstance startProcess(String key) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key);
        return processInstance;
    }

    @Override
    public void auditTask(String processId, Map<String, Object> map) {
        List<Task> list = taskService.createTaskQuery().processInstanceId(processId).list();
        Task task = list.get(0);
        taskService.complete(task.getId(), map);
    }

    @Override
    public String createFlowChart(String processId) {
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();

        //流程走完的不显示图
        if (pi == null) {
            HistoricVariableInstance hvi = processEngine.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(processId).singleResult();
            return String.format("流程已完成【%s】", hvi.getValue());
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();

        //得到正在执行的Activity的Id
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows,
                     engconf.getActivityFontName(), engconf.getLabelFontName(),
                     engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true)) {
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int len;
            while ((len = in.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            return Base64.getEncoder().encodeToString(baos.toByteArray());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void loadFlowFile(List<String> list) {
        log.info("流程文件正在加载");
        // 部署流程文件
        list.forEach(path -> repositoryService.createDeployment().addClasspathResource(path).deploy());
        log.info("流程文件加载完毕");
    }

}
